using System;
using System.Xml;
using System.Xml.Serialization;
using log4net;
using CodeMadeSimple.Common.Validation;
using CodeMadeSimple.Xml;

namespace EnvRide.Core.ObjectModel
{
    /// <summary>
    /// This class represents an attribute override.
    /// </summary>
    public class AttributeOverride : Override
    {
        #region Fields

        private string _attributeName;
        private string _newValue;
        private AttributeOverrideMode _overrideMode;

        #endregion

        #region Properties

        /// <summary>
        /// Name of the attribute that will be overriden.
        /// </summary>
        public string AttributeName
        {
            get { return _attributeName; }
            set { _attributeName = value; }
        }

        /// <summary>
        /// Attribute's new value.
        /// </summary>

        public string NewValue
        {
            get { return _newValue; }
            set { _newValue = value; }
        }

        /// <summary>
        /// Override's mode.
        /// </summary>
        public AttributeOverrideMode OverrideMode
        {
            get { return _overrideMode; }
            set { _overrideMode = value; }
        }

        #endregion

        #region Methods

        public override void ReadXml(XmlReader reader)
        {
            base.ReadXml(reader);

            AttributeName = reader.GetAttribute("attributeName");
            NewValue = reader.GetAttribute("newValue");

            String overrideMode = reader.GetAttribute("overrideMode");

            if (!String.IsNullOrEmpty(overrideMode))
            {
                switch (overrideMode)
                {
                    case "ReplaceValue":
                        OverrideMode = AttributeOverrideMode.ReplaceValue;
                        break;

                    case "AppendAttribute":
                        OverrideMode = AttributeOverrideMode.AppendAttribute;
                        break;

                    case "RemoveAttribute":
                        OverrideMode = AttributeOverrideMode.RemoveAttribute;
                        break;
                }
            }
        }

        public override void WriteXml(XmlWriter writer)
        {
            base.WriteXml(writer); 

            if (!String.IsNullOrEmpty(AttributeName))
                writer.WriteAttributeString("attributeName", AttributeName);

            if (!String.IsNullOrEmpty(NewValue))
                writer.WriteAttributeString("newValue", NewValue);

            writer.WriteAttributeString("overrideMode", OverrideMode.ToString());
        }

        /// <summary>
        /// Process the override.
        /// </summary>
        /// <param name="args">Processing arguments.</param>
        /// <param name="elem">Element found by the Xpath expression.</param>
        protected override void Process(EnvRideProcessArguments args, XmlElement elem)
        {
            if (String.IsNullOrEmpty(AttributeName))
                throw new InvalidOperationException("Must specify the attribute's name!");

            XmlAttribute att = null;

            switch (OverrideMode)
            {
                case AttributeOverrideMode.ReplaceValue:

                    // Replaces the attribute value
                    att = elem.GetAttributeNode(AttributeName);

                    if (att != null)
                    {
                        if (EnvRideProject.Logger.IsDebugEnabled)
                            EnvRideProject.Logger.DebugFormat("Replacing {0} value from \"{1}\" to \"{2}\".", XmlUtils.GetFullPath(att), att.Value, NewValue);

                        att.Value = args.ParseValue(NewValue);
                    }
                    else
                    {
                        args.HasErrors = true;
                        EnvRideProject.Logger.ErrorFormat("The attribute \"{0}\" was not found at element \"{1}\"!", AttributeName, XmlUtils.GetFullPath(elem));
                    }

                    break;

                case AttributeOverrideMode.AppendAttribute:

                    // Creates the new attribute
                    XmlAttribute newAtt = args.Document.CreateAttribute(AttributeName);
                    newAtt.Value = args.ParseValue(NewValue);

                    // Appends to the element
                    elem.Attributes.Append(newAtt);

                    if (EnvRideProject.Logger.IsDebugEnabled)
                        EnvRideProject.Logger.DebugFormat("Appending {0}=\"{1}\".", XmlUtils.GetFullPath(newAtt), NewValue);

                    break;

                case AttributeOverrideMode.RemoveAttribute:

                    att = elem.GetAttributeNode(AttributeName);

                    if (att != null)
                    {
                        if (EnvRideProject.Logger.IsDebugEnabled)
                            EnvRideProject.Logger.DebugFormat("Removing {0}.", XmlUtils.GetFullPath(att));

                        // Removes the attribute
                        elem.RemoveAttribute(AttributeName);
                    }
                    else
                    {
                        args.HasErrors = true;
                        EnvRideProject.Logger.ErrorFormat("The attribute \"{0}\" was not found at element \"{1}\"!", AttributeName, XmlUtils.GetFullPath(elem));
                    }

                    break;
            }
        }

        protected override string GetDefaultName()
        {
            String defaultName = "AttributeOverride";

            if (Environment != null && Environment.AttributeOverrides != null)
            {
                defaultName += "[" + Environment.AttributeOverrides.IndexOf(this) + "]";
            }

            return defaultName;
        }

        protected override void AddValidationMessages(CodeMadeSimple.Common.Validation.ValidationResult result)
        {
            base.AddValidationMessages(result);

            if (String.IsNullOrEmpty(AttributeName))
            {
                result.AddMessage(new RequiredValueValidationMessage("AttributeName"));
            }
        }

        #endregion
    }
}